
#include <utest.h>

#include <hello/serial/serial.h>

#include <chrono>


/// CLASS ABC
class ABC
{
public:
    int id;
    std::string name;
    std::unordered_map<int, std::string> info;

public:
    ABC(int _id, std::string _name, std::unordered_map<int, std::string> _info) :
            id(_id), name(std::move(_name)), info(std::move(_info))
    {
    }

    ABC() : id(-1), name(), info() {}
    ABC(const ABC&) = default;
    ABC(ABC&&) noexcept = default;

public:
    std::string to_string()
    {
        std::string ans = "[id]" + std::to_string(id) + " [name]" + name + " [info]";
        for (auto& [k, v] : info)
            ans.append(std::to_string(k)).append("->").append(v).append("|");
        return ans;
    }

public:
    template<typename Archiver>
    void archive(Archiver &arch)
    {
        arch(id, name, info);
    }
};

/// CLASS InDataABC
class InDataABC
{
public:
    int x;
    double y;
    ABC abc;
    std::unordered_set<std::string> words;

public:
    InDataABC(int _x, double _y, ABC _abc, std::unordered_set<std::string> _words) :
            x(_x), y(_y), abc(std::move(_abc)), words(std::move(_words))
    {
    }

    InDataABC() : x(-1), y(0.0), abc(), words() {}
    InDataABC(const InDataABC&) = default;
    InDataABC(InDataABC&&) noexcept = default;

public:
    std::string to_string()
    {
        std::string ans = "[x]" + std::to_string(x) + " [y]" + std::to_string(y) + " [abc]" + abc.to_string() + " [words]{";
        for (auto& w : words)
        {
            ans.append(w).append(",");
        }
        return ans;
    }

public:
    template<typename Archiver>
    void archive(Archiver &arch)
    {
        arch(x, y, abc, words);
    }
};


TEST(t001, c001, 1)
{
    InDataABC data1 { 15, 34.121, { 232131, "Tom Cris", { { 1, "erws" }, { 9, "sewqq" } } }, { "word1", "word2", "word3" } };
    println(data1.to_string());
    std::string out;
    hello::serializer serializer(out);
    serializer(data1);
    println(out);

    hello::deserializer deserializer(out);
    InDataABC data2;
    deserializer(data2);
    println(data2.to_string());
    println(&data1);
    println(&data2);
}

TEST(t001, c002, 1)
{
    auto print_mp = [](std::unordered_map<std::string, std::unordered_set<int>>& _mp)
    {
        for (const auto& [k, v] : _mp)
        {
            std::cout << k << "->";
            for (const auto& i : v)
            {
                std::cout << i << " ";
            }
            std::cout << std::endl;
        }
    };

    std::unordered_map<std::string, std::unordered_set<int>> mp {
        { "English", { 1, 2, 3 } },
        { "Biology", { 213, 312, 23 } }
    };
    println(&mp);
    print_mp(mp);

    std::string out;
    hello::serializer serializer(out);
    serializer(mp);

    println("------------");
    
    hello::deserializer deserializer(out);
    std::unordered_map<std::string, std::unordered_set<int>> mpp;
    deserializer(mpp);
    println(&mpp);
    print_mp(mpp);
}

TEST(base, queue, 1)
{
    std::queue<std::string> que;
    que.push("aaa");
    que.push("bbbb");
    que.push("cccccc");
    que.push("dd");

    println(&que);

    std::string buf;
    hello::serializer serializer(buf);
    serializer(que);

    std::queue<std::string> que1;
    hello::deserializer deserializer(buf);
    deserializer(que1);

    println(&que1);
    while (!que1.empty())
    {
        println(que1.front());
        que1.pop();
    }
}

TEST(base, stack, 1)
{
    std::stack<std::string> stk;
    stk.push("aaa");
    stk.push("bbbb");
    stk.push("cccccc");
    stk.push("dd");

    println(&stk);

    std::string buf;
    hello::serializer serializer(buf);
    serializer(stk);

    std::stack<std::string> stk1;
    hello::deserializer deserializer(buf);
    deserializer(stk1);

    println(&stk1);
    while (!stk1.empty())
    {
        println(stk1.top());
        stk1.pop();
    }
}

TEST(base, tuple, 1)
{
    std::queue<int> que;
    que.push(1); que.push(2); que.push(3); que.push(4);
    std::tuple<std::queue<int>, int, std::string> tp { que, 412, "Tom Cris" };
    println(&tp);
    println(std::get<0>(tp).size(), std::get<0>(tp).front(), std::get<1>(tp), std::get<2>(tp));

    std::string buf;
    hello::serializer ser(buf);
    ser(tp);
    println(buf);

    std::tuple<std::queue<int>, int, std::string> tp1;
    hello::deserializer dser(buf);
    dser(tp1);

    println(&tp1);
    println(std::get<0>(tp1).size(), std::get<0>(tp1).front(), std::get<1>(tp1), std::get<2>(tp1));
}

TEST(benchmark, 100000, 1)
{
    InDataABC data1 { 15, 34.121, { 232131, "Tom Cris", { { 1, "erws" }, { 9, "sewqq" } } }, { "word1", "word2", "word3" } };

    // serialize
    auto st1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 100000; ++i)
    {
        std::string str;
        hello::serializer ser(str);
        ser(data1);
    }
    auto ed1 = std::chrono::high_resolution_clock::now();
    println(std::chrono::duration_cast<std::chrono::milliseconds>(ed1 - st1).count(), "ms");

    std::string str;
    hello::serializer ser(str);
    ser(data1);

    // deserialize
    st1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 100000; ++i)
    {
        hello::deserializer der(str);
        InDataABC dat;
        der(dat);
    }
    ed1 = std::chrono::high_resolution_clock::now();
    println(std::chrono::duration_cast<std::chrono::milliseconds>(ed1 - st1).count(), "ms");
}


